package ua.maxicash.controller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import ua.maxicash.editors.CityEditor;
import ua.maxicash.editors.JobTypeEditor;
import ua.maxicash.exceptions.ImageUploadException;
import ua.maxicash.service.*;
import ua.maxicash.vo.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.IOException;
import java.sql.Time;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;

@Controller
public class LoginController {

    private static final Logger logger = Logger.getLogger(LoginController.class);

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(City.class, new CityEditor(geoService));
        binder.registerCustomEditor(JobType.class, new JobTypeEditor(workService));
    }

    @Autowired
    private WorkService workService;

    @Autowired
    private GeoService geoService;

    @Autowired
    ConfirmationService confirmationService;

    @Autowired
    private AppService appService;

    @Autowired
    private WorkerService workerService;

    @Autowired
    private EmployerService employerService;

    @RequestMapping(value = "/users/{idUser}")
    public String showUserPage(@PathVariable Long idUser) {

        SystemUser su = appService.getSystemUserById(idUser);

        if (su instanceof Employer) {
            return "redirect:/employers/" + idUser;
        }
        if (su instanceof Worker) {
            return "redirect:/workers/" + idUser;
        }

        return "error";
    }

    @RequestMapping({"/", "/homepage"})
    public String redirect() {

        //todo brout-hack with ""
        if (SecurityContextHolder.getContext().getAuthentication().getCredentials() != "") {
            User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            SystemUser su = appService.getSystemUserByEmail(user.getUsername());

            if (su instanceof Employer) {
                logger.info("Employer " + user.getUsername() + " is redirecting to home page");
                return "redirect:/employers/" + su.getIdUser();
            }
            if (su instanceof Worker) {
                logger.info("Worker " + user.getUsername() + " is redirecting to home page");
                return "redirect:/workers/" + su.getIdUser();
            }
        } else {
            logger.info("User is not authenticated. Redirecting to login page");
            return "login";
        }

        return "login";

    }

    @RequestMapping(value = "/registrationWorker")
    public String registerWorker(Model model) {

        model.addAttribute("worker", new Worker());
        model.addAttribute("jobTypes", workService.listJobTypes());

        return "registrationWorker";
    }

    //todo revisit and test
    @PreAuthorize("hasRole('ROLE_WORKER')")
    @RequestMapping("workers/{idWorker}/edit")
    public String editWorker(@PathVariable Long idWorker, Model model) {

        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        SystemUser su = appService.getSystemUserByEmail(user.getUsername());

        if (su.getIdUser().equals(idWorker)) {

            Worker worker = (Worker) su;
            model.addAttribute("worker", worker);
            Set<JobType> jobTypes = new HashSet<JobType>();
            Set<JobType> workersJobTypes = new HashSet<JobType>();
            jobTypes.addAll(workService.listJobTypes());
            workersJobTypes = worker.getJobTypes();
            jobTypes.removeAll(workersJobTypes);

            model.addAttribute("jobTypes", jobTypes);
            model.addAttribute("workersJobTypes", workersJobTypes);
            model.addAttribute("worker", worker);

            for (WorkersFreeTime wft : worker.getScheduleList()) {
                switch (wft.getDayOfWeek()) {
                    case 0:
                        model.addAttribute("monday", wft);
                        break;
                    case 1:
                        model.addAttribute("tuesday", wft);
                        break;
                    case 2:
                        model.addAttribute("wednesday", wft);
                        break;
                    case 3:
                        model.addAttribute("thursday", wft);
                        break;
                    case 4:
                        model.addAttribute("friday", wft);
                        break;
                    case 5:
                        model.addAttribute("saturday", wft);
                        break;
                    case 6:
                        model.addAttribute("sunday", wft);
                        break;
                    default:
                        break;

                }
            }

            logger.info(su.getEmail() + " redirected to the editWorker page");
            return "editWorker";
        } else {

            logger.error(su.getEmail()
                    + " is not an instance of Worker or tried to edit not his page");
            return "redirect:/error";
        }
    }

    @RequestMapping(value = "/registrationEmployer")
    public String registerEmployer(Model model) {

        model.addAttribute("employer", new Employer());
        model.addAttribute("jobTypes", workService.listJobTypes());

        return "registrationEmployer";
    }

    @RequestMapping(value = "/submitWorker")
    public String submitWorker() {
        return "homepage";
    }

    /**
     * Redirecting user to the login page
     *
     * @return login view name
     */
    @RequestMapping(value = {"/login"})
    public String login() {
        return "login";
    }

    @RequestMapping(value = "/registration/registerWorker")
    public String regWorker(@Valid Worker worker, BindingResult bindingResult, Model model,
                            @RequestParam(value = "file", required = false) MultipartFile image,
                            HttpServletRequest req) {

        if (bindingResult.hasErrors()) {
            return "registrationWorker";
        }

        PasswordEncoder encoder = new Md5PasswordEncoder();
        worker.setPassword(encoder.encodePassword(worker.getPassword(), null));

        Set<WorkersFreeTime> freeTimes = new HashSet<WorkersFreeTime>();
        DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");

        for (int i = 0; i < 7; i++) {
            try {
                if (!req.getParameter("startTime" + i).isEmpty()
                        && !req.getParameter("endTime" + i).isEmpty()) {
                    WorkersFreeTime wft = new WorkersFreeTime();
                    wft.setDayOfWeek(i);
                    wft.setStartTime(new Time(timeFormat.parse(
                            req.getParameter("startTime" + i)).getTime()));
                    wft.setEndTime(new Time(timeFormat.parse(
                            req.getParameter("endTime" + i)).getTime()));
                    freeTimes.add(wft);
                }
            } catch (ParseException e) {
                logger.error(e);
                return "registrationWorker";
            }

        }

        worker.setScheduleList(freeTimes);

        try {
            if (image != null && !image.isEmpty()) {
                validateImage(image);
                byte[] bytes = image.getBytes();
                worker.setAvatar(bytes);
            }
        } catch (ImageUploadException e) {
            bindingResult.reject(e.getMessage());
            return "registrationWorker";
        } catch (IOException e) {
            bindingResult.reject(e.getMessage());
            return "registrationWorker";
        }

        worker.setEnabled(false);
        confirmationService.addToLoginMap(worker.getEmail());
        workerService.addWorker(worker);

        model.addAttribute("URL", "/login");
        model.addAttribute("message","Check your email for finishing the registration process");
        return "redirection";

    }

    @RequestMapping("/firstlogin/{email}/{idRecovery}")
    public String changePassForm(@PathVariable String email, @PathVariable Long idRecovery, Model model) {
        if (confirmationService.isInLoginMap(email, idRecovery)) {
            confirmationService.enableUser(email);
            model.addAttribute("URL", "/login");
            model.addAttribute("message","Thank you for registration!");
            return "redirection";
        } else {
            return "error";
        }

    }

    private void validateImage(MultipartFile image) {
        if (!image.getContentType().equals("image/jpeg")) {
            throw new ImageUploadException("Only JPG images are accepted");
        }
    }

    @RequestMapping(value = "/registration/registerEmployer")
    public String regEmployer(@Valid Employer employer, BindingResult bindingResult, Model model,
                              @RequestParam(value = "file", required = false) MultipartFile image,
                              HttpServletRequest req) {

        if (bindingResult.hasErrors()) {
            return "registrationEmployer";
        }

        PasswordEncoder encoder = new Md5PasswordEncoder();
        employer.setPassword(encoder.encodePassword(employer.getPassword(), null));

        try {
            if (image != null && !image.isEmpty()) {
                validateImage(image);
                byte[] bytes = image.getBytes();
                employer.setAvatar(bytes);
            }
        } catch (ImageUploadException e) {
            bindingResult.reject(e.getMessage());
            return "registrationEmployer";
        } catch (IOException e) {
            bindingResult.reject(e.getMessage());
            return "registrationEmployer";
        }

        employer.setEnabled(false);
        confirmationService.addToLoginMap(employer.getEmail());
        employerService.addEmployer(employer);

        model.addAttribute("URL", "/login");
        model.addAttribute("message","Check your email for finishing the registration process");
        return "redirection";

    }

    @RequestMapping(value = "/saveEditedWorker")
    @ResponseBody
    public String saveEditedWorker(@Valid Worker worker, BindingResult bindingResult, Model model,
                                   @RequestParam(value = "file", required = false) MultipartFile image,
                                   HttpServletRequest req) {

        if (bindingResult.hasErrors()) {
            return "{\"isOk\" : false}";
        }


        Set<WorkersFreeTime> freeTimes = new HashSet<WorkersFreeTime>();
        DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");

        for (int i = 0; i < 7; i++) {
            try {
                if (!req.getParameter("startTime" + i).isEmpty()
                        && !req.getParameter("endTime" + i).isEmpty()) {
                    WorkersFreeTime wft = new WorkersFreeTime();
                    wft.setDayOfWeek(i);
                    wft.setStartTime(new Time(timeFormat.parse(
                            req.getParameter("startTime" + i)).getTime()));
                    wft.setEndTime(new Time(timeFormat.parse(
                            req.getParameter("endTime" + i)).getTime()));
                    freeTimes.add(wft);
                }
            } catch (ParseException e) {
                logger.error(e);
            }

        }

        worker.setScheduleList(freeTimes);

        try {
            if (image != null && !image.isEmpty()) {
                validateImage(image);
                byte[] bytes = image.getBytes();
                worker.setAvatar(bytes);
            }
        } catch (ImageUploadException e) {
            bindingResult.reject(e.getMessage());
            return "registrationWorker";
        } catch (IOException e) {
            bindingResult.reject(e.getMessage());
            return "registrationWorker";
        }

        workerService.updateWorker(worker);

        return "{\"isOk\" : true}";
    }

    @RequestMapping(value = "/saveEditedEmployer")
    @ResponseBody
    public String saveEditedEmployer(@Valid Employer employer, BindingResult bindingResult, Model model,
                                     @RequestParam(value = "file", required = false) MultipartFile image,
                                     HttpServletRequest req) {

        if (bindingResult.hasErrors()) {
            return "{\"isOk\" : false}";
        }

        try {
            if (image != null && !image.isEmpty()) {
                validateImage(image);
                byte[] bytes = image.getBytes();
                employer.setAvatar(bytes);
            }
        } catch (ImageUploadException e) {
            bindingResult.reject(e.getMessage());
            return "registrationWorker";
        } catch (IOException e) {
            bindingResult.reject(e.getMessage());
            return "registrationWorker";
        }

        employerService.updateEmployer(employer);

        return "{\"isOk\" : true}";

    }

}
